"""
des:
author: 邵宁
date: 2022-09-15
"""
import os
import re
import random
import time
import json
import base64
import ctypes
import inspect
import rpmfile
import requests
import threading
from contextlib import closing
from util import event_run, msg_manage, path, ssh2server
from util.msg_template import MsgTemplateGitPull, MsgTemplateForRpmRelease, MsgTemplateFileDownload


# import signal
# import atexit

def stop_thread(thread):
    def _async_raise(tid, exc_type):
        tid = ctypes.c_long(tid)
        if not inspect.isclass(exc_type):
            exc_type = type(exc_type)
        res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exc_type))
        if res == 0:
            pass
            # raise ValueError("invalid thread id")
        elif res != 1:
            ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)
            raise SystemError("PyThreadState_SetAsyncExc failed")

    _async_raise(thread.ident, SystemExit)


class SingletonType(type):
    _instance_lock = threading.Lock()

    def __call__(cls, *args, **kwargs):
        if not hasattr(cls, "_instance"):
            with SingletonType._instance_lock:
                if not hasattr(cls, "_instance"):
                    cls._instance = super(SingletonType, cls).__call__(*args, **kwargs)
        return cls._instance


class SessionTemplate:
    def __init__(self, *args, **kwargs):
        self.session_id = "t_" + time.strftime("%Y%m%d_%H%M%S")
        self.session_name = kwargs.get("session_name")
        self.session_obj = threading.Thread(target=self.work, args=args, kwargs=kwargs)
        self.session_percent = 0
        self.session_status = "waiting"  # [waiting\running\stop\trigger]
        self.session_mutex = kwargs.get("mutex", "")
        self.session_trigger = kwargs.get("trigger", [])  # 设定触发器条件（前置session任务状态变成stop）
        self.session_token = kwargs.get("session_token")
        self.session_log = []

    def start(self):
        self.session_status = "running"
        self.session_obj.start()

    def stop(self):
        stop_thread(self.session_obj)
        self.session_status = "stop"

    def status(self):
        pass

    def log_write(self, **kwargs):
        log_one = {
            "time": time.strftime("%Y-%m-%d %H:%M:%S"),
            "log_type": kwargs.get("log_type", "str"),
            "log_info": kwargs.get("log_info", ""),
        }
        self.session_log.append(log_one)

    def restart(self):
        pass

    def work(self, *args, **kwargs):
        raise NotImplemented

    def callback(self):
        print(self.session_id, "ok")

    def get_info(self):
        return {
            "session_id": self.session_id,
            "session_name": self.session_name,
            "session_percent": self.session_percent,
            "session_status": self.session_status,
            "session_mutex": self.session_mutex,
            "session_trigger": "、".join(self.session_trigger),
            "session_token": self.session_token,
        }


def session_check_mutex(session_run, mutex):  # 检查互斥锁不通过返回True
    if mutex in [""]:
        return False
    for session_one in session_run:
        if session_one.session_mutex == mutex:
            return True
    return False


def session_check_trigger(session_run, session_one):  # 检查前置任务是否完成，不通过返回True
    for session_id in session_one.session_trigger:
        for s_o in session_run:
            if s_o.session_id == session_id:
                if s_o.session_status == "running":  # 遇到前置任务正在运行中，直接返回True
                    return True
                elif s_o.session_status == "fail":
                    session_one.session_status = "fail"
    return False


class SessionManage(SessionTemplate):
    def work(self, *args, **kwargs):  # s_l_m_obj -> session_list_manage_obj
        s_l_m_obj = args[0]
        while self.session_status == "running":
            for s_o_i, s_o in enumerate(s_l_m_obj.thread_list_running):  # 清除掉已经完成的任务
                if not s_o.session_obj.isAlive():
                    s_o.callback()
                    s_o.stop()
                if s_o.session_status != "running":
                    s_l_m_obj.thread_list_running.pop(s_o_i)
            for session_one in s_l_m_obj.thread_list_waiting:
                if session_one.session_status == "fail":
                    s_l_m_obj.thread_list_waiting.remove(session_one)
                    continue
                if len(s_l_m_obj.thread_list_running) < s_l_m_obj.session_running_max:
                    if session_check_mutex(s_l_m_obj.thread_list_running, session_one.session_mutex):  # 遇到互斥锁，跳过
                        continue
                    if session_check_trigger(s_l_m_obj.thread_list_running, session_one):
                        continue
                    s_l_m_obj.thread_list_waiting.remove(session_one)
                    session_one.start()
                    s_l_m_obj.thread_list_running.append(session_one)
                else:
                    break
            # print(len(s_l_m_obj.thread_list_running))
            if len(s_l_m_obj.thread_list_running) != 0:
                print("任务列表：", [s_o.session_percent for s_o in s_l_m_obj.thread_list_running])
            time.sleep(1)


class SessionDownload(SessionTemplate):
    def work(self, *args, **kwargs):
        download_url = kwargs.get("download_url", "")
        filename = kwargs.get("download_name", "").replace('\\', '/').replace('/', '_')
        filename = filename if filename != "" else os.path.basename(download_url)
        filename = filename if filename != "" else path.get_file_name_random()
        download_path = os.path.join(kwargs.get("download_path", ""), filename)
        path.dir_init(download_path)
        self.session_name = "文件下载-" + kwargs.get("download_url", "").split('/')[-1]
        db = kwargs.get("db", None)
        session_token = kwargs.get("session_token", "")
        db.update_session_info(session_token, "正在下载文件：" + filename)
        msg = MsgTemplateFileDownload()
        msg.set_key_value("msg_str", "开始执行下载任务" + filename)
        msg.set_key_value("msg_type", "download_file")
        msg_manage.msg_write(msg.get_json(), session_token)
        try:
            with open(download_path, 'wb') as file_handler:
                with closing(requests.get(kwargs.get("download_url"), stream=True)) as response:
                    chunk_size = 1024  # 单次请求最大值
                    content_size = int(response.headers['content-length'])  # 内容体总大小
                    data_count = 0
                    for data in response.iter_content(chunk_size=chunk_size):
                        file_handler.write(data)
                        data_count = data_count + len(data)
                        now_jd = (data_count / content_size) * 100
                        self.session_percent = int(now_jd)
            msg.set_key_value("msg_str", "下载完成：" + filename)
            db.update_session_info(session_token, "下载完成：" + filename)
        except Exception as e:
            self.session_status = "error"
            msg.set_key_value("msg_str", "下载失败：" + filename)
            db.update_session_info(session_token, "下载失败：" + filename)
        self.session_status = "stop"
        msg_manage.msg_write(msg.get_json(), session_token)


class SessionDownloadForSPEC(SessionTemplate):
    def work(self, *args, **kwargs):
        download_url = kwargs.get("download_url", "")
        filename = kwargs.get("download_name", "").replace('\\', '/').replace('/', '_')
        filename = filename if filename != "" else path.get_file_name_random()
        download_path = os.path.join(kwargs.get("download_path", ""), filename)
        path.dir_init(download_path)
        self.session_name = "SPEC文件下载-" + filename
        db = kwargs.get("db", None)
        session_token = kwargs.get("session_token", "")
        db.update_session_info(session_token, "正在下载文件：" + filename)
        msg = MsgTemplateFileDownload()
        msg.set_key_value("msg_str", "开始执行下载任务" + filename)
        msg.set_key_value("msg_type", "download_file_spec")
        msg_manage.msg_write(msg.get_json(), session_token)
        try:
            self.session_percent = "下载中"
            res = requests.get(download_url)
            b64_byt = json.loads(res.content.decode("utf-8")).get("content")
            spec_str = base64.b64decode(b64_byt).decode('utf-8')
            with open(download_path, 'w', encoding="utf-8") as file_handler:
                file_handler.write(spec_str)
            msg.set_key_value("msg_str", "下载完成：" + filename)
            db.update_session_info(session_token, "下载完成：" + filename)
        except Exception as e:
            self.session_status = "error"
            msg.set_key_value("msg_str", "下载失败：" + filename)
            db.update_session_info(session_token, "下载失败：" + filename)
        self.session_percent = "下载完成"
        self.session_status = "stop"
        msg_manage.msg_write(msg.get_json(), session_token)


class SessionGitEuler4CVE(SessionTemplate):
    def work(self, *args, **kwargs):
        self.session_percent = "Git拉取中..."
        code_name = kwargs.get("code_name", "")
        code_branch = kwargs.get("code_branch", "openEuler-20.03-LTS-SP1")
        path_session_one = kwargs.get("path_session_one", "")
        db = kwargs.get("db", None)
        session_token = kwargs.get("session_token", "")

        self.session_name = "Git分支-" + code_branch

        db.update_session_info(session_token, "Git拉取中")
        db.update_session_status(session_token, "处理中")
        msg = MsgTemplateGitPull()
        msg.set_key_value("msg_str", "开始执行Git拉取任务")
        msg.set_key_value("msg_type", "git_pull_start")
        msg_manage.msg_write(msg.get_json(), session_token)
        if code_name == "" or path_session_one == "":
            self.session_status = "error"
            return
        try:
            git_url = f"https://gitee.com/src-openeuler/{code_name}.git"
            task_command = f"git clone -b {code_branch} {git_url} {path_session_one}/source_gitee/{code_branch}/{code_name}"
            event_run.cmd_run(task_command, f"{path_session_one}/log_cmd_run")
            self.session_status = "stop"
            self.session_percent = "Git拉取完成！"
            db.update_session_info(session_token, "Git拉取完成")
            msg.set_key_value("msg_str", "拉取完成：" + code_branch)
            msg.set_key_value("msg_type", "git_pull_success")
            msg_manage.msg_write(msg.get_json(), session_token)
        except Exception as e:
            self.session_status = "error"
            self.session_percent = "Git拉取失败！"
            db.update_session_info(session_token, "Git拉取失败")
            db.update_session_status(session_token, "失败")
            msg.set_key_value("msg_str", "拉取失败：" + code_branch)
            msg.set_key_value("msg_type", "git_pull_fail")
            msg_manage.msg_write(msg.get_json(), session_token)


class SessionRpmRelease(SessionTemplate):
    def work(self, *args, **kwargs):
        self.session_percent = "RPM包数据提取中..."
        self.session_name = "【RPM包提取】"
        session_token = kwargs.get("session_token", "")
        filepath = kwargs.get("filepath", "")
        src_path = kwargs.get("src_path", "")
        path.dir_init(os.path.join(src_path, "temp"))
        db = kwargs.get("db", None)
        msg = MsgTemplateForRpmRelease()
        msg.set_key_value("msg_str", "RPM包正在提取中：" + os.path.basename(filepath))
        msg.set_key_value("msg_type", "release_rpm_source")
        msg_manage.msg_write(msg.get_json(), session_token)
        try:
            db.update_session_info(session_token, "RPM包正在提取中")
            with rpmfile.open(filepath) as rpm:
                for member in rpm.getmembers():
                    self.session_percent = "正在提取：" + member.name
                    fd = rpm.extractfile(member.name)
                    with open(os.path.join(src_path, member.name), "wb") as f:
                        f.write(fd.read())
            self.session_percent = "全部提取完成！"
            db.update_session_info(session_token, "RPM包提取完成")
            msg.set_key_value("msg_str", "RPM包提取完成：" + os.path.basename(filepath))
        except Exception as e:
            self.session_status = "error"
            self.session_percent = "RPM包提取失败！"
            db.update_session_info(session_token, "RPM包提取失败")
            msg.set_key_value("msg_str", "RPM包提取失败：" + os.path.basename(filepath))
        msg_manage.msg_write(msg.get_json(), session_token)


class SessionDownloadForCompatibilityReport(SessionTemplate):
    def work(self, *args, **kwargs):
        self.session_percent = "兼容性报告生成任务创建中..."
        # self.session_name = "【兼容性报告生成】"
        session_token = kwargs.get("session_token", "")
        task_koji_link = kwargs.get("task_koji_link", "")
        download_path = kwargs.get("download_path", "")
        path.dir_init(os.path.join(download_path, "temp"))
        db = kwargs.get("db", None)
        msg = MsgTemplateForRpmRelease()
        msg.set_key_value("msg_str", "兼容性报告生成中，预计保存位置：" + os.path.basename(download_path))
        msg.set_key_value("msg_type", "release_rpm_source")
        msg_manage.msg_write(msg.get_json(), session_token)
        try:
            db.update_session_info(session_token, "兼容性报告生成中")
            for i in range(100):
                time.sleep(1)
                self.session_percent = str(i)
            self.session_percent = "兼容性报告生成完成！"
            db.update_session_info(session_token, "兼容性报告已生成")
            msg.set_key_value("msg_str", "兼容性报告已生成：" + os.path.basename(download_path))  # TODO 改成html的地址
        except Exception as e:
            self.session_status = "error"
            self.session_percent = "兼容性报告生成失败！"
            db.update_session_info(session_token, "兼容性报告生成失败")
            msg.set_key_value("msg_str", "兼容性报告生成失败")
        msg_manage.msg_write(msg.get_json(), session_token)


class SessionPackage2Rpm(SessionTemplate):
    def work(self, *args, **kwargs):
        self.session_percent = "正在打包中..."
        session_token = kwargs.get("session_token", "")
        host = kwargs.get("host", "")
        username = kwargs.get("username", "")
        password = kwargs.get("password", "")
        msg_man = kwargs.get("msg_man", "")
        filepath = kwargs.get("filepath", "")
        script_package_dir2rpm = kwargs.get("script_package_dir2rpm", "")
        msg_man.push_msg(session_token, f"任务开始执行，正在尝试连接服务器{host}。。。")
        try:
            # for _i in range(10):
            #     time.sleep(1)
            #     self.session_percent = f"{str(_i)}/{str(10)}/{self.session_mutex}"
            #     msg_man.push_msg(session_token, str(_i))
            ssh = ssh2server.ConnectSSH(host, username, password)
            ssh.connect()
            msg_man.push_msg(session_token, f"连接服务器{host}成功，上传打包文件。。。")
            ssh.file_put(filepath, "/root/rpmbuild.tar.gz")
            msg_man.push_msg(session_token, f"文件已上传，开始执行脚本。。。")
            package_file_get = False
            print(script_package_dir2rpm)
            for cmd_str in script_package_dir2rpm.split('\n'):
                cmd_str = cmd_str.rstrip('\r')
                ssh_res = ">>> " + cmd_str + '\n' + ssh.exec(cmd_str).rstrip()
                msg_man.push_msg(session_token, ssh_res)
                if package_file_get:
                    continue
                # ssh_ls_str = ssh.exec("ls -1 /root/rpmbuild/SRPMS |awk '{print i$0}' i='/root/rpmbuild/SRPMS/'",
                #                       is_show=True)
                r = re.findall("Wrote: (/root/rpmbuild/SRPMS/.*\.src\.rpm)", ssh_res)
                if r:
                    save_dir = os.path.join("static/temp_package_file_save", path.get_file_name_random())
                    path.dir_init(os.path.join(save_dir, "aa"))
                    local_file_path = ssh.file_get(r[0], save_dir)
                    print("取回打包路径########>>>", r[0], local_file_path)
                    msg_man.push_msg(session_token, "打包文件取回路径：" + local_file_path)
                    msg_man.push_link(session_token, link=local_file_path, filename=os.path.basename(local_file_path))
                    package_file_get = True
                self.session_percent = cmd_str
            msg_man.push_msg(session_token, "打包完成", is_end=True)
            self.session_percent = "打包完成！"
        except Exception as e:
            self.session_status = "error"
            self.session_percent = "打包失败！"
            msg_man.push_msg(session_token, "打包失败", is_end=True)


class MsgManNone:
    def push_msg(self, *args, **kwargs):
        pass


class SessionCompatibilityReportGenerate(SessionTemplate):
    def work(self, *args, **kwargs):
        self.session_percent = "兼容性报告正在生成中..."
        session_token = kwargs.get("session_token", "")
        host = kwargs.get("host", "")
        username = kwargs.get("username", "")
        password = kwargs.get("password", "")
        msg_man = kwargs.get("msg_man", MsgManNone())
        script_run_str = kwargs.get("script_run_str", "")
        msg_man.push_msg(session_token, f"兼容性报告生成任务开始执行，正在尝试连接服务器{host}。。。")
        try:
            # for _i in range(10):
            #     time.sleep(1)
            #     self.session_percent = f"{str(_i)}/{str(10)}/{self.session_mutex}"
            #     msg_man.push_msg(session_token, str(_i))
            ssh = ssh2server.ConnectSSH(host, username, password)
            ssh.connect()
            msg_man.push_msg(session_token, f"连接服务器{host}成功，开始执行脚本。。。")
            package_file_get = False
            for cmd_str in script_run_str.split('\n'):
                cmd_str = cmd_str.rstrip('\r')
                ssh_res = ">>> " + cmd_str + '\n' + ssh.exec(cmd_str).rstrip()
                msg_man.push_msg(session_token, ssh_res)
                if package_file_get:
                    continue
                # ssh_ls_str = ssh.exec("ls -1 /root/rpmbuild/SRPMS |awk '{print i$0}' i='/root/rpmbuild/SRPMS/'",
                #                       is_show=True)
                r = re.findall("Wrote: (/root/rpmbuild/SRPMS/.*\.src\.rpm)", ssh_res)
                if r:
                    save_dir = os.path.join("static/temp_compatibility_report_save", path.get_file_name_random())
                    path.dir_init(os.path.join(save_dir, "aa"))
                    local_file_path = ssh.file_get(r[0], save_dir)
                    print("取回报告路径########>>>", r[0], local_file_path)
                    msg_man.push_msg(session_token, "报告文件取回路径：" + local_file_path)
                    msg_man.push_link(session_token, link=local_file_path, filename=os.path.basename(local_file_path))
                    package_file_get = True
                self.session_percent = cmd_str
            msg_man.push_msg(session_token, "报告生成完成", is_end=True)
            self.session_percent = "报告生成完成！"
        except Exception as e:
            self.session_status = "error"
            self.session_percent = "报告生成失败！"
            msg_man.push_msg(session_token, "报告生成失败，错误原因：" + str(e), is_end=True)


class SessionTest(SessionTemplate):
    def work(self, *args, **kwargs):
        # print(args, kwargs)
        a = random.randint(6, 13)
        for _i in range(a):
            self.session_percent = f"{str(_i)}/{str(a)}/{self.session_mutex}"
            time.sleep(1)
        self.session_status = "stop"


class SessionListManage(metaclass=SingletonType):
    def __init__(self):
        self.session_manage_thread_obj = None
        self.session_self = None
        self.session_list = []  # 保存所有会话，用来备份  TODO 后面持久化
        self.thread_list_running = []  # 正在运行的任务
        self.thread_list_waiting = []  # 等待运行的任务
        self.session_running_max = 10  # 能够同时运行的最大数量
        self.session_manage_thread()  # 用于管理的线程

    def session_manage_thread(self):
        self.session_manage_thread_obj = SessionManage(self)
        self.session_manage_thread_obj.start()

    def get_session_info_by_session_token(self, session_token):
        session_info_list = []
        # print(self.session_list)
        for sess_one in self.session_list:
            # print(sess_one.session_token, session_token)
            if sess_one.session_token == session_token:
                session_info_list.append(sess_one.get_info())
        return session_info_list

    def join(self, session_one):
        self.session_list.append(session_one)
        self.thread_list_waiting.append(session_one)

    def get_index_of_wait_queue(self, session_token):
        for idx, sess_one in enumerate(self.thread_list_waiting):
            if sess_one.session_token == session_token:
                return idx
        return -1

    def session_status_list(self):
        pass

    def session_start_one(self):
        pass

    def session_restart_one(self):
        pass

    def session_stop_one(self):
        pass

    def session_status_one(self):
        pass

    def session_delete_one(self):
        pass


if __name__ == '__main__':
    # s = SessionDownload("http://10.1.123.157/ns86/BaseOS/Packages/ModemManager-1.18.2-1.el8.src.rpm")
    # s.start()
    # time.sleep(1)
    # print(s.session_percent)
    #
    s = SessionListManage()
    for i in range(5):
        # time.sleep(1)
        if i % 2 == 0:
            ss = SessionTest(mutex="aa")
        else:
            ss = SessionTest()
        s.join(ss)

    # for i in range(10):
    #     ss = SessionDownload(download_url="https://cdn-lxs3.b.qianxin.com/lxpmcpublic/b4dcecd4-016f-4288-82b9-c4045e4cb852.exe")
    #     s.join(ss)
    ss = SessionDownload(
        download_url="https://cdn-lxs3.b.qianxin.com/lxpmcpublic/b4dcecd4-016f-4288-82b9-c4045e4cb852.exe")
    s.join(ss)
    sss = SessionDownload(
        download_url="https://cdn-lxs3.b.qianxin.com/lxpmcpublic/b4dcecd4-016f-4288-82b9-c4045e4cb852.exe",
        trigger=[ss.session_id])
    s.join(sss)

    for i in range(100):
        # time.sleep(1)
        if i % 2 == 0:
            ss = SessionTest(mutex="aa")
        else:
            ss = SessionTest()
        s.join(ss)
